﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using FLUDP.Utility;

namespace FLUDP
{
    public class FludpClient
    {
        public IPAddress ipAddress;
        public int port;
        public bool connected = false;

        public Socket clientSocket;
        public EndPoint epServer;
        private byte[] byteData = new byte[1024];

        public EventHandler eConnect, eReceive, eDisconnect;

        public string name = "Client";

        public bool Connect(string ipAddress, int port)
        {
            if (connected)
                return false;

            if (IPAddress.TryParse(ipAddress, out this.ipAddress))
                return Connect(this.ipAddress, port);
            else
                return false;
        }
        public bool Connect(IPAddress ipAddress, int port)
        {
            if (connected)
                return false;

            try
            {
                this.ipAddress = ipAddress;
                this.port = port;

                //Using UDP sockets
                clientSocket = new Socket(AddressFamily.InterNetwork,
                    SocketType.Dgram, ProtocolType.Udp);

                //Server is listening on port
                IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);

                epServer = (EndPoint)ipEndPoint;

                SendData(Command.Join);

                clientSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref epServer, new AsyncCallback(OnReceive), null);

                // Not definitely sure whether we're connected or not
                if(eConnect != null)
                    eConnect(this, EventArgs.Empty);

                return true;
            }
            catch (SocketException)
            {
                connected = false;
                return false;
            }
        }

        public void OnSend(IAsyncResult ar)
        {
            try
            {
                clientSocket.EndSend(ar);
            }
            catch (Exception ex) 
            {
                throw ex;
            }
        }
        public void OnReceive(IAsyncResult ar)
        {
            try
            {
                clientSocket.EndReceive(ar);
                connected = true;

                //Convert the bytes received into an object of type Data
                Data msgReceived = new Data(byteData);

                eReceive(this, new FludpReceiveEventArgs(msgReceived.cmdCommand, msgReceived.strName, msgReceived.strMessage, epServer));

                byteData = new byte[1024];

                //Start listening to receive more data from the user
                clientSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref epServer,
                                           new AsyncCallback(OnReceive), null);
            }
            catch (SocketException)
            {
                connected = false;
            }
        }

        public void SendData()
        {
            SendData(Command.Null);
        }
        public void SendData(Command c)
        {
            SendData(c, null);
        }
        public void SendData(Command c, string m)
        {
            SendData(c, name, m);
        }
        public void SendData(Command c, string n, string m)
        {
            Data d = new Data();
            d.strName = n;
            d.strMessage = m;
            d.cmdCommand = c;
            SendData(d);
        }
        public void SendData(Data d)
        {
            try
            {
                byte[] message = d.ToByte();
                clientSocket.BeginSendTo(message, 0, message.Length,
                    SocketFlags.None, epServer, new AsyncCallback(OnSend), null);
            }
            catch (ObjectDisposedException) { }
        }
    }
}
